package com.xuecheng.content.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.fasterxml.jackson.annotation.JsonRawValue;
import com.xuecheng.common.domain.code.CommonErrorCode;
import com.xuecheng.common.enums.content.CourseAuditEnum;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.common.util.JsonUtil;
import com.xuecheng.content.agent.SystemApiAgent;
import com.xuecheng.content.common.constant.ContentErrorCode;
import com.xuecheng.content.entity.CourseBase;
import com.xuecheng.content.entity.CoursePub;
import com.xuecheng.content.entity.CoursePubMsg;
import com.xuecheng.content.mapper.CoursePubMapper;
import com.xuecheng.content.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ObjectUtils;

import java.time.LocalDateTime;

/**
 * <p>
 * 课程发布 服务实现类
 * </p>
 *
 * @author xuan
 */
@Slf4j
@Service
public class CoursePubServiceImpl extends ServiceImpl<CoursePubMapper, CoursePub> implements CoursePubService {
    @Autowired
    private CourseBaseService courseBaseService;
    @Autowired
    private TeachplanService teachplanService;
    @Autowired
    private CoursePubService coursePubService;
    @Autowired
    private CoursePubMsgService coursePubMsgService;
    @Autowired
    private CourseMarketService courseMarketService;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    public CourseBaseServiceImpl courseBaseServiceImpl;


    @Value("${course.publish.exchange}")
    private String exchange;

    @Value("${course.publish.routingkey}")
    private String routingkey;

    /**课程发布
     * 业务分析：
     *   1.判断关键数据
     *   2.判断业务数据
     *       课程基本信息
     *           判断是否存在
     *           判断是否是同一家机构
     *           判断是否删除
     *           判断审核状态
     *               只能是审核通过的状态
     *   3.创建课程发布数据和发布消息数据
     *       课程发布数据
     *           课程基本信息 课程营销 课程计划 课程教师信息
     *       发布消息表
     *   4.发送消息给mq
     *       消息必须是有业务唯一标识：courseid
     *       发送消息：发布消息表
     *           转为json字符串
     *
     * @param courseId 课程基本信息id
     * @param
     * @param
     */
/*    public void publishll(Long courseId, Long companyId, boolean isResend) {


        CoursePubMsg coursePubMsg;
        if (isResend) {
            // 如果是课程发布重新发送的操作，无需产生coursePub数据和coursepubMsg
            //查询课程发布表  给课程发布记录表添加数据
            LambdaQueryWrapper<CoursePub> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(CoursePub::getCourseId, courseId)
                    .eq(CoursePub::getCompanyId, companyId);
            CoursePub coursePub = this.getOne(queryWrapper);

            coursePubMsg = new CoursePubMsg();
            coursePubMsg.setCourseId(coursePub.getCourseId());
            coursePubMsg.setPubName(coursePub.getName());
            coursePubMsg.setPubStatus(CoursePubMsg.UNSENT);
            coursePubMsg.setPubId(coursePub.getId());
            coursePubMsg.setCompanyId(coursePub.getCompanyId());
        } else {
            //判断数据 可引用之前方法
            CoursePub coursePub = courseBaseServiceImpl.generateCoursePub(courseId, companyId, true);
            //      发布消息表
            coursePubMsg = new CoursePubMsg();

            coursePubMsg.setPubId(coursePub.getId());
            coursePubMsg.setCourseId(coursePub.getCourseId());
            coursePubMsg.setPubStatus(CoursePubMsg.UNSENT);
            coursePubMsg.setCompanyId(coursePub.getCompanyId());
            coursePubMsg.setPubName(coursePub.getName());

            //为避免前端发送请求重复 先查询消息表是否存在
            LambdaQueryWrapper<CoursePubMsg> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(CoursePubMsg::getPubId,coursePub.getId());
            int count = pubMsgService.count(wrapper);
            if (count<1) {
                boolean result = pubMsgService.save(coursePubMsg);
                if (!result) {
                    ExceptionCast.cast(ContentErrorCode.E_120206);
                }
            }
        }
        //  4.发送消息给mq
        //      消息必须是有业务唯一标识：courseid
        //      发送消息：发布消息表
        //          转为json字符串
        CorrelationData correlationData = new CorrelationData(courseId.toString());

        correlationData.getFuture().addCallback(
                confirm -> {
                    //交换机相应成功
                    if (confirm.isAck()) {
                        // 修改课程基本信息审核状态和发布消息表的状态
                        execCangeStatusData(new Long(correlationData.getId()));
                    }
                },
                //交换机相应失败的回调方法
                throwable -> {
                    log.error("课程发布消息通知失败,courseId:{},exception:{}"
                            ,courseId,throwable.getMessage());
                }
        );
        String msgJsonString = JsonUtil.objectTojson(coursePubMsg);
        rabbitTemplate.convertAndSend(exchange,routingkey,msgJsonString,correlationData);
    }
     */



    /**业务分析：
     1.判断关键数据
     courseBaseId companyId
     2.判断业务数据
     课程基础信息
     判断是否存在
     判断是否是同一家机构
     判断是否删除
     判断审核状态
     必须是：审核通过
     3.保存数据的业务数据和消息数据
     业务数据：CoursePub数据进行保存
     消息数据：CoursePubMsg数据进行保存
     4.发送消息给MQ
     指定：交换机名称、routingkey、发送消息CoursePubMsg数据、CorrelationData指定成功和失败方法
     * @param courseId 课程基本信息id
     * @param companyId 公司id
     */
    @Transactional
    public void publish(Long courseId, Long companyId) {
        //1.判断关键数据
        //     courseBaseId companyId
        if (ObjectUtils.isEmpty(courseId)||
                ObjectUtils.isEmpty(companyId)
        ) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }
        // 2.判断业务数据
        //     课程基础信息
        //         判断是否存在
        //         判断是否是同一家机构
        //         判断是否删除
        //         判断审核状态
        //             必须是：审核通过
        // 3.保存数据的业务数据和消息数据
        //     业务数据：CoursePub数据进行保存
        CoursePub coursePub = courseBaseServiceImpl.generateCoursePub(courseId, companyId, true);



        //     消息数据：CoursePubMsg数据进行保存
        // 为了保证前端操作时，数据库中的消息表数据不会出现唯一约束的问题，需要在添加消息数据前查询是否有数据
        //         如果有不添加
        //         如果没有添加数据
        LambdaQueryWrapper<CoursePubMsg> msgQueryWrapper = new LambdaQueryWrapper<>();
        msgQueryWrapper.eq(CoursePubMsg::getPubId,coursePub.getId());
        CoursePubMsg coursePubMsg = coursePubMsgService.getOne(msgQueryWrapper);


        if (ObjectUtils.isEmpty(coursePubMsg)) {
            coursePubMsg = new CoursePubMsg();
            coursePubMsg.setPubId(coursePub.getId());
            coursePubMsg.setPubName(coursePub.getName());
            coursePubMsg.setPubStatus(CoursePubMsg.UNSENT);
            coursePubMsg.setCourseId(courseId);
            coursePubMsg.setCompanyId(companyId);

            boolean result = coursePubMsgService.save(coursePubMsg);

            if (!result) {
                ExceptionCast.cast(ContentErrorCode.E_120206);
            }
        }

        // 4.发送消息给MQ
        //     指定：交换机名称、routingkey、发送消息CoursePubMsg数据、CorrelationData指定成功和失败方法

        /*
         * 参数：
         *   1.交互机名称
         *   2.routingkey名称
         *   3.发送的消息
         *   4.CorrelationData指定成功和失败方法
         * */
        String msgJsonString = JsonUtil.objectTojson(coursePubMsg);

        // 构造方法中需要指定消息的id值（消息唯一标识）
        CorrelationData correlationData = new CorrelationData(courseId.toString());

        correlationData.getFuture().addCallback(
                // 交互机响应成功
                confirm -> {
                    boolean ack = confirm.isAck();
                    // ack 交换机成功接收消息
                    if (ack) {
                        // 交换机成功接受消息后，要修改本地的数据状态
                        execCangeStatusData(new Long(correlationData.getId()) );
                    }
                },
                // 交互机响应失败
                throwable -> {
                    log.error("发送课程发布的消息失败，失败的标识数据：{} , error: {}",correlationData.getId(),throwable.getMessage());
                }
        );

        rabbitTemplate.convertAndSend(exchange,routingkey,msgJsonString,correlationData);

    }

    /*
    * 消息成功发送并修改课程相关数据
        课程基本信息的审核状态
        发布消息表的状态
    * */

    private void execCangeStatusData(Long courseId) {
        LambdaUpdateWrapper<CourseBase> updateWrapper = new LambdaUpdateWrapper<>();

        updateWrapper.set(CourseBase::getAuditStatus, CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode());
        updateWrapper.set(CourseBase::getChangeDate, LocalDateTime.now());
        updateWrapper.eq(CourseBase::getId, courseId);

        boolean baseResult = courseBaseService.update(updateWrapper);
        if (!baseResult) {
            //这里并不适合抛异常 可以用日志记录下来
            log.info("课程基本信息修改发布后的状态失败，courseId：{}",courseId);
        }
        //修改发布消息表的状态 将发布状态该为1
        LambdaUpdateWrapper<CoursePubMsg> msgUpdateWrapper = new LambdaUpdateWrapper<>();

        msgUpdateWrapper.set(CoursePubMsg::getPubStatus, CoursePubMsg.SENT);
        msgUpdateWrapper.eq(CoursePubMsg::getCourseId, courseId);

        boolean msgResult = coursePubMsgService.update(msgUpdateWrapper);

        if (!msgResult) {
            log.info("课程发布消息数据修改发布后的状态失败，courseId：{}",courseId);
        }



    }

}
